/**
  ******************************************************************************
  * @author     Chris
  * @since      2024/6/26 23:00
  *
  * @file       pd_rtc.c
  * @brief      Xxx hardware driver.
  *
  * @note       This file contains the hardware driver for the Xxx.
  *
  * @warning    None.
  ******************************************************************************
  * Change Logs:
  *   Date          Author       Notes
  *   2024/6/26     Chris        the first version
  *
  ******************************************************************************
  */


#include <stm32f4xx_ll_bus.h>
#include <stm32f4xx_ll_pwr.h>
#include <stm32f4xx_ll_rcc.h>
#include <stm32f4xx_ll_rtc.h>
#include <string.h>
#include "pd_rtc.h"

#define RTC_FLAG 0x5A5A5A5A

static void RTC_init_(RealTimeClock *this, uint16_t *RTC_datetime) {
    LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

    // Enable write access to Backup domain
    LL_PWR_EnableBkUpAccess();

    // Enable LSE/LSI, and wait ready
    //    LL_RCC_LSE_Enable();
    //    while (LL_RCC_LSE_IsReady() != SET);
    LL_RCC_LSI_Enable();
    while (LL_RCC_LSI_IsReady() != SET);
    LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSI);

    /* RTC init */
    LL_RTC_InitTypeDef LL_RTC_InitStruct = {0};
    LL_RTC_StructInit(&LL_RTC_InitStruct);
    LL_RTC_InitStruct.HourFormat      = LL_RTC_HOURFORMAT_24HOUR;
    LL_RTC_InitStruct.AsynchPrescaler = 0;
    LL_RTC_InitStruct.SynchPrescaler  = 32767;
    LL_RTC_Init(RTC, &LL_RTC_InitStruct);

    LL_RCC_EnableRTC();

    // Initialize RTC and set the Time and Date
    if (LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR19) != RTC_FLAG) {
        LL_RTC_WaitForSynchro(RTC);
        return;
    }

    this->vptr->set_datetime(this, RTC_datetime);

    // 写入标志位
    LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR19, RTC_FLAG);

}

static void RTC_setDatetime_(RealTimeClock *this, uint16_t *RTC_datetime) {
    memcpy(this->RTC_datetime, RTC_datetime, sizeof(this->RTC_datetime) / sizeof(this->RTC_datetime[0]));

    /* Data init */
    LL_RTC_DateTypeDef LL_RTC_DateStruct;
    LL_RTC_DATE_StructInit(&LL_RTC_DateStruct);
    LL_RTC_DateStruct.WeekDay = this->RTC_datetime[WEEK];
    LL_RTC_DateStruct.Day     = this->RTC_datetime[DAY];
    LL_RTC_DateStruct.Month   = this->RTC_datetime[MONTH];
    LL_RTC_DateStruct.Year    = this->RTC_datetime[YEAR];
    LL_RTC_DATE_Init(RTC, LL_RTC_FORMAT_BCD, &LL_RTC_DateStruct);

    /* Time init */
    LL_RTC_TimeTypeDef LL_RTC_TimeStruct;
    LL_RTC_TIME_StructInit(&LL_RTC_TimeStruct);
    LL_RTC_TimeStruct.TimeFormat = LL_RTC_TIME_FORMAT_AM_OR_24;
    LL_RTC_TimeStruct.Hours      = this->RTC_datetime[HOUR];
    LL_RTC_TimeStruct.Minutes    = this->RTC_datetime[MINUTE];
    LL_RTC_TimeStruct.Seconds    = this->RTC_datetime[SECOND];
    LL_RTC_TIME_Init(RTC, LL_RTC_FORMAT_BCD, &LL_RTC_TimeStruct);
}

static void RTC_readTime_(RealTimeClock *this) {
    this->RTC_datetime[YEAR]   = LL_RTC_DATE_GetYear(RTC);
    this->RTC_datetime[MONTH]  = LL_RTC_DATE_GetMonth(RTC);
    this->RTC_datetime[DAY]    = LL_RTC_DATE_GetDay(RTC);
    this->RTC_datetime[HOUR]   = LL_RTC_TIME_GetHour(RTC);
    this->RTC_datetime[MINUTE] = LL_RTC_TIME_GetMinute(RTC);
    this->RTC_datetime[SECOND] = LL_RTC_TIME_GetSecond(RTC);
    this->RTC_datetime[WEEK]   = LL_RTC_DATE_GetWeekDay(RTC);
}

static uint32_t RTC_getTime_(const RealTimeClock *this) {
    return LL_RTC_TIME_Get(RTC);
}

static uint32_t RTC_getDate_(const RealTimeClock *this) {
    return LL_RTC_DATE_Get(RTC);
}

void RealTimeClock_constructor(RealTimeClock *this) {
    static struct RealTimeClockVtbl vtbl = {
            .init         = &RTC_init_,
            .set_datetime = &RTC_setDatetime_,
            .get_datetime = &RTC_readTime_,
            .get_time     = &RTC_getTime_,
            .get_date     = &RTC_getDate_,
    };

    this->vptr = &vtbl;
}
